package com.vortex.cloud.zhsw.xinyang.service;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.concurrent.*;

/**
 * @Description
 * @Author ChenY
 * @Date 2022/5/9 15:31
 **/
public class ThreadPoolDemoService {

    public static void main( String[] args ){
        System.out.println("直接提交队列...................");
        testSynchronousQueue();
        System.out.println("有界的任务队列...................");
        testArrayBlockingQueue();
        System.out.println("无界的任务队列...................");
        testLinkedBlockingQueue();
        System.out.println("优先任务队列...................");
        testPriorityBlockingQueue();
    }


    /**
     * 直接提交队列：
     * 使用SynchronousQueue队列，提交的任务不会被保存，总是会马上提交执行。
     * 如果用于执行任务的线程数量小于maximumPoolSize，则尝试创建新的进程，
     * 如果达到maximumPoolSize设置的最大值，则根据你设置的handler执行拒绝策略。
     * 因此这种方式你提交的任务不会被缓存起来，而是会被马上执行，在这种情况下，你需要对你程序的并发量有个准确的评估，才能设置合适的maximumPoolSize数量，否则很容易就会执行拒绝策略；
     */
    public static void testSynchronousQueue(){
        ExecutorService pool = createPool(1, 2, 1000, TimeUnit.MILLISECONDS,
                new SynchronousQueue<Runnable>(),Executors.defaultThreadFactory(),new ThreadPoolExecutor.AbortPolicy());
        int count = 3;
        for(int i=0;i<count;i++) {
            pool.execute(new ThreadTask1());
        }
        pool.shutdown();
    }

    /**
     * 有界的任务队列：
     * 使用ArrayBlockingQueue有界任务队列，
     * 若有新的任务需要执行时，线程池会创建新的线程，直到创建的线程数量达到corePoolSize时，则会将新的任务加入到等待队列中。
     * 若等待队列已满，即超过ArrayBlockingQueue初始化的容量，则继续创建线程，直到线程数量达到maximumPoolSize设置的最大线程数量，
     * 若大于maximumPoolSize，则执行拒绝策略。
     * 在这种情况下，线程数量的上限与有界任务队列的状态有直接关系，
     * 如果有界队列初始容量较大或者没有达到超负荷的状态，线程数将一直维持在corePoolSize以下，反之当任务队列已满时，则会以maximumPoolSize为最大线程数上限。
     */
    public static void testArrayBlockingQueue(){
        ExecutorService pool = createPool(1, 2, 1000, TimeUnit.MILLISECONDS, new ArrayBlockingQueue<Runnable>(10),Executors.defaultThreadFactory(),new ThreadPoolExecutor.AbortPolicy());
        int count = 3;
        for(int i=0;i<count;i++) {
            pool.execute(new ThreadTask1());
        }
        pool.shutdown();
    }

    /**
     * 无界的任务队列：
     * 使用无界任务队列，线程池的任务队列可以无限制的添加新的任务，
     * 而线程池创建的最大线程数量就是你corePoolSize设置的数量，也就是说在这种情况下maximumPoolSize这个参数是无效的，
     * 哪怕你的任务队列中缓存了很多未执行的任务，当线程池的线程数达到corePoolSize后，就不会再增加了；
     * 若后续有新的任务加入，则直接进入队列等待，
     * 当使用这种任务队列模式时，一定要注意你任务提交与处理之间的协调与控制，不然会出现队列中的任务由于无法及时处理导致一直增长，直到最后资源耗尽的问题。
     */
    public static void testLinkedBlockingQueue(){
        ExecutorService pool = createPool(1, 2, 1000, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>(),Executors.defaultThreadFactory(),new ThreadPoolExecutor.AbortPolicy());
        int count = 3;
        for(int i=0;i<count;i++) {
            pool.execute(new ThreadTask1());
        }
        pool.shutdown();
    }

    /**
     * 优先任务队列：
     *  除了第一个任务直接创建线程执行外，其他的任务都被放入了优先任务队列，按优先级进行了重新排列执行，且线程池的线程数一直为corePoolSize，也就是只有一个。
     *  通过运行的代码我们可以看出PriorityBlockingQueue它其实是一个特殊的无界队列，它其中无论添加了多少个任务，线程池创建的线程数也不会超过corePoolSize的数量，
     *  只不过其他队列一般是按照先进先出的规则处理任务，而PriorityBlockingQueue队列可以自定义规则根据任务的优先级顺序先后执行。
     */
    public static void testPriorityBlockingQueue(){
        ExecutorService pool = createPool(1, 2, 1000, TimeUnit.MILLISECONDS, new PriorityBlockingQueue<Runnable>(),Executors.defaultThreadFactory(),new ThreadPoolExecutor.AbortPolicy());
        int count = 20;
        for(int i=0;i< count;i++) {
            pool.execute(new ThreadTask2(i));
        }
        pool.shutdown();
    }

    /**
     * 创建线程池：
     *  线程吃线程数量的设置没有一个明确的指标，根据实际情况，只要不是设置的偏大和偏小都问题不大，结合下面这个公式即可
     *  Nthreads=线程数量
     *  Ncpu=CPU数量
     *  Ucpu=目标CPU的使用率，0<=Ucpu<=1
     *  W/C=任务等待时间与任务计算时间的比率
     *  Nthreads = Ncpu*Ucpu*(1+W/C)
     *
     * @param corePoolSize
     *  指定了线程池中的线程数量，它的数量决定了添加的任务是开辟新的线程去执行，还是放到workQueue任务队列中去；
     * @param maximumPoolSize
     *  指定了线程池中的最大线程数量，这个参数会根据你使用的workQueue任务队列的类型，决定线程池会开辟的最大线程数量；
     * @param keepAliveTime
     *  当线程池中空闲线程数量超过corePoolSize时，多余的线程会在多长时间内被销毁；
     * @param unit
     *  keepAliveTime的单位
     * @param workQueue
     *  任务队列，被添加到线程池中，但尚未被执行的任务；它一般分为直接提交队列、有界任务队列、无界任务队列、优先任务队列几种；
     * @param threadFactory
     *  线程工厂，用于创建线程，一般用默认即可；也可以通过实现ThreadFactory接口进行自定义
     * @param handler
     *  拒绝策略；当任务太多来不及处理时，如何拒绝任务；
     *      1、AbortPolicy策略：该策略会直接抛出异常，阻止系统正常工作；
     *      2、CallerRunsPolicy策略：如果线程池的线程数量达到上限，该策略会把任务队列中的任务放在调用者线程当中运行；
     *      3、DiscardOledestPolicy策略：该策略会丢弃任务队列中最老的一个任务，也就是当前任务队列中最先被添加进去的，马上要被执行的那个任务，并尝试再次提交；
     *      4、DiscardPolicy策略：该策略会默默丢弃无法处理的任务，不予任何处理。当然使用此策略，业务场景中需允许任务的丢失；
     *      5、自定义拒绝策略：实现RejectedExecutionHandler接口
     */
    public static ExecutorService createPool(int corePoolSize,
                    int maximumPoolSize,
                    long keepAliveTime,
                    TimeUnit unit,
                    BlockingQueue<Runnable> workQueue,
                    ThreadFactory threadFactory,
                    RejectedExecutionHandler handler){
        return new ThreadPoolExecutor(corePoolSize,maximumPoolSize,keepAliveTime,unit,workQueue,threadFactory,handler);
    }


    @NoArgsConstructor
    public static class ThreadTask1 implements Runnable{

        @Override
        public void run() {
            System.out.println(Thread.currentThread().getName());
        }
    }

    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    public static class ThreadTask2 implements Runnable,Comparable<ThreadTask2>{

        private int priority;

        /**
         * 当前对象和其他对象做比较，当前优先级大就返回-1，优先级小就返回1,值越小优先级越高
         * @param o
         * @return
         */
        @Override
        public int compareTo(ThreadTask2 o) {
            return  this.priority>o.priority?-1:1;
        }

        @Override
        public void run() {
            try {
                //让线程阻塞，使后续任务进入缓存队列
                Thread.sleep(1000);
                System.out.println("priority:"+this.priority+",ThreadName:"+Thread.currentThread().getName());
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }

        }
    }


}
